﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Windows.Forms;

using AdaptationProject.Properties;
using AdaptationProject.Forms;
using SharedTypes;
using System.Xml;

namespace AdaptationProject.Utils
{
    /// <summary>
    /// Класс отвечающий за анализ содержимого XML файлов
    /// согласно внутреннему формату.
    /// </summary>
    public class XMLParamParser : IXMLParser
    {
        #region Members
        /// <summary>
        /// Текущий загруженный ХМЛ документ.
        /// </summary>
        private XDocument currentDocument;

        /// <summary>
        /// Путь к текущему ХМЛ документу.
        /// </summary>
        private string currentPath;
        #endregion // Members

        #region Properties
        /// <summary>
        /// Свойство определяющее, загружен ли в парсер
        /// какой либо документ в данный момент.
        /// </summary>
        public bool IsLoaded { get; private set; }
        #endregion // Properties

        #region Constructor
        /// <summary>
        /// Конструктор.
        /// </summary>
        public XMLParamParser()
        {
            IsLoaded = false;
        }
        #endregion // Constructor

        #region Methods
        /// <summary>
        /// Загружает ХМЛ файл.
        /// </summary>
        /// <param name="path">Абсолютный путь к ХМЛ файлу.</param>
        /// <returns>Истина если загрузка произошла успешно,
        /// иначе - ложь.</returns>
        public bool Load(string path)
        {
            bool result = true;
            try
            {
                currentPath = path;
                currentDocument = XDocument.Load(path);
                IsLoaded = true;
            }
            catch
            {
                result = false;
            }
            return result;
        }

        /// <summary>
        /// Создаёт шаблон файла описания алгоритма.
        /// </summary>
        /// <param name="path">Абсолютный путь для размещения шаблона.</param>
        public void CreateTemplateDocument(string path)
        {
            if (ValidatePath(path))
            {
                CreateTemplateDoc(path);
            }
            else
            {
                MessageBox.Show(Resources.XMLParser_Incorrect_Extension);
            }
        }

        /// <summary>
        /// Сохраняет документ по текущему пути.
        /// </summary>
        public void Save()
        {
            SaveTo(currentPath);
        }

        /// <summary>
        /// Сохраняет документ.
        /// </summary>
        /// <param name="newPath">Абсолютный путь.</param>
        public void SaveTo(string newPath)
        {
            if (ValidatePath(newPath))
            {
                currentDocument.Save(newPath);
            }
            else
            {
                throw new Exception(Resources.XMLParser_Incorrect_Extension);
            }
        }

        /// <summary>
        /// Устанавливает заголовок файла описания алгоритма исполнительного файла.
        /// </summary>
        /// <param name="algorithmName">Имя алгоритма.</param>
        /// <param name="exePath">Путь к исполняемому файлу, необходимому для функционирования алгоритма
        /// (может быть пустым).</param>
        public void SetHeader(string algorithmName, string algorithmFullPath, string exePath)
        {

            XElement[] newElements = {
                                     new XElement("AlgName", algorithmName),
                                     new XElement("AlgPath", algorithmFullPath),
                                     new XElement("ExePath", exePath) 
                                 };
            currentDocument.Descendants("Header").First().Add(newElements);
        }

        /// <summary>
        /// Добавляет параметр алгоритма исполнительного файла, подлежащий адаптации.
        /// </summary>
        /// <param name="orderNum">Порядковый номер параметра.</param>
        /// <param name="name">Имя параметра.</param>
        /// <param name="Conditional link">Условное связывание с верхним уровнем.</param>
        /// <param name="description">Описание назначение параметра.</param>
        /// <param name="acceptableRange">Допустимые значения 
        /// (строка, организованная согласно внутреннему формату). </param>
        /// <param name="defValue">Значение по умолчанию.</param>
        /// <param name="defValue">Является ли параметр ключем.</param>
        /// <param name="defValue">Тип параметра.</param>
        public void AddParameter(int orderNum,
                                 int linkedNum,
                                 string name,
                                 string conditionLink,
                                 string shortName,
                                 string description,
                                 IList<AcceptableValue> acceptableRange,
                                 string valueEnd,
                                 string separator,
                                 string defValue,
                                 bool isKey,
                                 int type)
        {
            XElement newElements = new XElement("Parameter",
                                                new XElement("OrderNum", orderNum),
                                                new XElement("LinkedNum", linkedNum),
                                                new XElement("ConditionLink", conditionLink),
                                                new XElement("Name", name),
                                                new XElement("ShortName", shortName),
                                                new XElement("Description", description),
                                                new XElement("AcceptableRanges"),
                                                new XElement("ValueEnd", valueEnd),
                                                new XElement("Separator", separator),
                                                new XElement("DefaultValue", defValue),
                                                new XElement("IsKey", isKey),
                                                new XElement("Type", type));

            foreach (AcceptableValue acceptableValue in acceptableRange)
            {
                XElement newElement = new XElement("AcceptableRange",
                                                    new XElement("Range", acceptableValue.TextValues),
                                                    new XElement("Percent", acceptableValue.Percent));

                newElements.Descendants("AcceptableRanges").Last().Add(newElement);
            }

            currentDocument.Descendants("Parameters").First().Add(newElements);
        }

        /// <summary>
        /// Возвращает имя алгоритма, указанное в его файле описания.
        /// </summary>
        /// <returns>Имя алгоритма исполнительного файла.</returns>
        public string GetAlgorithmName()
        {
            string result = string.Empty;
            result = currentDocument.Descendants("AlgName").First().Value;
            return result;
        }

        /// <summary>
        /// Возвращает путь к библиотеке, содержащей алгоритм исполнительного файла. 
        /// </summary>
        /// <returns>Путь к библиотеке.</returns>
        public string GetAlgorithmPath()
        {
            string result = string.Empty;
            result = currentDocument.Descendants("AlgPath").First().Value;
            return result;
        }

        /// <summary>
        /// Возвращает путь к исполняемому файлу алгоритма исполнительного файла. 
        /// </summary>
        /// <returns>Путь к исполняемому файлу.</returns>
        public string GetExePath()
        {
            string result = string.Empty;
            result = currentDocument.Descendants("ExePath").First().Value;
            return result;
        }

        /// <summary>
        /// Загружает список адаптируемых параметров алгоритма исполнительного файла.
        /// </summary>
        /// <returns>Список адаптируемых параметров.</returns>
        public IDictionary<int, LinkedParameter> GetAlgorithmParameters()
        {
            IDictionary<int, LinkedParameter> toRet = new Dictionary<int, LinkedParameter>();
            var parameters = from param in currentDocument.Descendants("Parameter")
                             orderby Int32.Parse(param.Element("OrderNum").Value) ascending
                             select param;

            foreach (XElement parameter in parameters)
            {
                byte orderNum = byte.Parse(parameter.Element("OrderNum").Value);
                string name = parameter.Element("Name").Value;
                string conditionLink = parameter.Element("ConditionLink").Value;
                string shortName = parameter.Element("ShortName").Value;
                string description = parameter.Element("Description").Value;
                string valueEnd = parameter.Element("ValueEnd").Value;
                string separator = parameter.Element("Separator").Value;
                object defValue = parameter.Element("DefaultValue").Value;
                bool isKey = bool.Parse(parameter.Element("IsKey").Value);
                int type = int.Parse(parameter.Element("Type").Value);
                int linkedNum = int.Parse(parameter.Element("LinkedNum").Value);

                ParamParser paramParser = new ParamParser();

                var acceptableRanges = from param in parameter.Descendants("AcceptableRange")
                                       select param;

                IList<AcceptableValue> acceptValues = new List<AcceptableValue>();

                foreach (XElement acceptableRange in acceptableRanges)
                {
                    AcceptableValue acceptValue = new AcceptableValue();

                    acceptValue.values = paramParser.Parse(acceptableRange.Element("Range").Value, type);
                    acceptValue.TextValues = acceptableRange.Element("Range").Value;
                    long boundary = 0;
                    Int64.TryParse(acceptableRange.Element("Percent").Value, out boundary);
                    acceptValue.Percent = boundary;

                    acceptValues.Add(acceptValue);
                }

                CmdParameter cmd = new CmdParameter(orderNum,
                                           name,
                                           conditionLink,
                                           shortName,
                                           description,
                                           acceptValues,
                                           valueEnd,
                                           separator,
                                           defValue,
                                           isKey,
                                           type);
                toRet.Add(int.Parse(parameter.Element("OrderNum").Value),
                          new LinkedParameter(linkedNum, cmd));
            }

            return toRet;
        }

        /// <summary>
        /// Создаёт шаблон документа описания алгоритма.
        /// </summary>
        /// <param name="path">Путь к документу.</param>
        private void CreateTemplateDoc(string path)
        {
            if (ValidatePath(path))
            {
                currentDocument = new XDocument(
                                                new XDeclaration("1.0", "utf-8", "yes"),
                                                new XElement("Algorithm",
                                                new XElement("Header"),
                                                new XElement("Parameters"))
                    );
                currentDocument.Save(path);
                IsLoaded = true;
            }
            else
            {
                throw new Exception(Resources.XMLParser_Incorrect_Extension);
            }
        }

        /// <summary>
        /// Проверяет соответствие файла формату ХМЛ.
        /// </summary>
        /// <param name="path">Путь к файлу.</param>
        private static bool ValidatePath(string path)
        {
            return path.EndsWith(".xml");
        }
        #endregion // Methods
    }
}
